namespace SS.Ynote.Classic
{
    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;
    using System.Xml;
    using System.Diagnostics;
    using System.Windows.Forms;
    using WeifenLuo.WinFormsUI.Docking;
    using FastColoredTextBoxNS;
    using System.Text.RegularExpressions;
    using System.IO;
    using SS.Ynote.Classic.Themes;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using SS.Ynote.Classic.AutoCompletion;
    using System.ComponentModel;

    #endregion

    public partial class Editor : WeifenLuo.WinFormsUI.Docking.DockContent
    {
        #region Constants
    
        List<AutocompleteItem> menuItemList = new List<AutocompleteItem>();
        TextStyle bluestyle = new TextStyle(Brushes.Blue,null,FontStyle.Regular);
        TextStyle greenstyle = new TextStyle(Brushes.Green, null, FontStyle.Regular);
        TextStyle redstyle = new TextStyle(Brushes.Red, null, FontStyle.Regular);
        private Style invisibleCharsStyle;
        FastColoredTextBoxNS.AutocompleteMenu menu;

        #endregion

        #region Constructor
        /// <summary>
        /// Ynote Host
        /// </summary>
        private IYnoteHost Host;

        public Editor(IYnoteHost host)
        {
            InitializeComponent();
            InitializeEvents();
            BuildAutocomplete(new General());
            invisibleCharsStyle = new InvisibleCharsRenderer(Pens.Gray);
            this.codebox.AllowDrop = true;
            Host = host;
            YnoteThemeReader.Apply(codebox, YnoteSettingsBase.CurrentTheme, Host.SyntaxHighlighter);
        }
        void BuildIMEList()
        {
            foreach (ImeMode mode in Enum.GetValues(typeof(ImeMode)).Cast<ImeMode>())
            {
                ToolStripMenuItem m = new ToolStripMenuItem();
                m.Text = mode.ToString();
                m.Click += new EventHandler(m_Click);
                miime.DropDownItems.Add(m);
            }
            GetMenuByName("NoControl", miime).Checked = true;
        }
        private void m_Click(object sender, EventArgs e) 
        {
            ToolStripMenuItem item = sender as ToolStripMenuItem;
            foreach (ToolStripMenuItem itm in miime.DropDownItems)
                itm.Checked = false;
            item.Checked = true;
            codebox.ImeMode = FromString<ImeMode>(item.Text);
        }
        /// <summary>
        /// Enum from string
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T FromString<T>(string value)
        {
            return (T)Enum.Parse(typeof(T), value);
        }
        /// <summary>
        /// Get Menu By name
        /// </summary>
        /// <param name="Name"></param>
        /// <returns></returns>
        ToolStripMenuItem GetMenuByName(string Name, ToolStripMenuItem parent)
        {
            foreach (ToolStripMenuItem c in parent.DropDownItems)
                if (c.Text == Name)
                    return c;
            return null;
        }
        private void Editor_DragDrop(object sender, DragEventArgs e)
        {
            string[] FileList = (string[])e.Data.GetData(DataFormats.FileDrop, false);
            foreach (string file in FileList)
                Host.OpenFile(file, 0);
        }
        /// <summary>
        /// Initialize Events
        /// </summary>
        private void InitializeEvents()
        {
            this.codebox.DragDrop += new DragEventHandler(Editor_DragDrop);
            this.codebox.DragEnter += new DragEventHandler(Editor_DragEnter);
            this.codebox.SelectionChangedDelayed += new EventHandler(codebox_SelectionChangedDelayed);
            this.codebox.MouseMove += new MouseEventHandler(codebox_MouseMove);
            this.codebox.MouseDown += new MouseEventHandler(codebox_MouseDown);
            this.codebox.TextChangedDelayed += new EventHandler<TextChangedEventArgs>(fctb_textchangeddelayed);
            this.codebox.LanguageChanged += new EventHandler(codebox_LanguageChanged);
            this.codebox.ShowLineNumbers = YnoteSettingsBase.ShowLineNumbers;
            this.codebox.LineInterval = YnoteSettingsBase.LineInterval;
            this.codebox.ShowFoldingLines = YnoteSettingsBase.ShowFoldingLines;
            this.codebox.CaretVisible = YnoteSettingsBase.ShowCaret;
            this.codebox.HighlightingRangeType = YnoteSettingsBase.HighlightStrategy;
            this.codebox.WordWrapMode = YnoteSettingsBase.WordWrapMode;
            this.codebox.LeftPadding = YnoteSettingsBase.PaddingWidth;
            this.codebox.TextAreaBorder = YnoteSettingsBase.TextAreaType;
        }
        private void Editor_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }
        private void codebox_LanguageChanged(object sender, EventArgs e)
        {
            this.BuildAutocomplete(AutoCompleter.AutoComplete(codebox.Language));
        }
        /// <summary>
        /// Undo Enabled
        /// </summary>
        public bool UndoEnabled
        {
            get { return codebox.UndoEnabled; }
        }
        /// <summary>
        /// Redo Enabled
        /// </summary>
        public bool RedoEnabled
        {
            get { return codebox.RedoEnabled; }
        }
        /// <summary>
        /// Paste Enabled
        /// </summary>
        /// <returns></returns>
        public bool IsPasteEnabled()
        {
            if (string.IsNullOrEmpty(Clipboard.GetText()))
                return false;
            else
                return true;
        }
        /// <summary>
        /// AutoComplete Menu
        /// </summary>
        public FastColoredTextBoxNS.AutocompleteMenu AutoCompleteMenu
        {
            get { return menu; }
        }
        /// <summary>
        /// Build AutoComplete Menu
        /// </summary>
        private void BuildAutocomplete(IAutoCompleteMenu imenu)
        {
            try
            {
                menu = new AutocompleteMenu(codebox);
                menu.Items.SetAutocompleteItems(AutoCompleter.BuildAutoCompleteMenu(imenu));
          
            }
            catch (Exception ex) { Console.WriteLine(ex.Message); }
        }
        #endregion

        #region Events
        private void closeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void cutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.Cut();
        }

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.Copy();
        }

        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.Undo();
        }

        private void pToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.Paste();
        }

        private void redoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.Redo();
        }
        private void Editor_Closing(object sender, FormClosingEventArgs e)
        {
            if (this.codebox.IsChanged == true)
            {
                DialogResult dialogResult = MessageBox.Show("Save Changes to " + this.Text + "?", "Save", MessageBoxButtons.YesNoCancel);
                if (dialogResult == DialogResult.Yes)
                {
                    SaveAs(e);
                    if(codebox.BindingContext != null)
                        codebox.CloseBindingFile();
                }
                else if (dialogResult == DialogResult.No)
                        codebox.CloseBindingFile();
                else if (dialogResult == DialogResult.Cancel)
                    e.Cancel = true;

            }
            
        }
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            LoadIContextMenuPlugin();
            BuildIMEList();
        }
        private void SaveAs(FormClosingEventArgs e)
        {
            if (!(this.Name == "Editor"))
                codebox.SaveToFile(this.Name, System.Text.Encoding.Default);
            else
            {
                SaveFileDialog s = new SaveFileDialog();
                s.Title = "Save As..";
                s.Filter = "All Files(*.*)|*.*|Text Files(*.txt)|*.txt|XML Files (*.xml)|*.xml|XML Schema Definition File(*.xsd)|*.xsd|Log File (*.log)|*.log|HTML Document (*.html),(*.xhtml),(*.shtml)|*.html;*.xhtml;*.shtml|ASP.NET File(*.asp),(*.aspx)|*.asp;*.aspx|PHP Document (*.php)|*.php|Cascading Style Sheet (*.css)|*.css|Javascript File (*.js)|*.js|QBasic File(*.bas)|*.bas|Visual Basic File (*.vb)|*.vb|Python File (*.py)|*.py|Ruby File(*.ruby)|*.ruby|Lua File(*.lua)|Flash Actionscript file(*.as)|*.as|C# Source File(*.cs)|*.cs|C Source File(*.c)|C++ Source File (*.cpp)|*.cpp|C++ Header File(*.h)|*.h";
                s.ShowDialog();
                if (!(string.IsNullOrEmpty(s.FileName)))
                    codebox.SaveToFile(s.FileName, System.Text.Encoding.Default);
                else
                    e.Cancel = true;
            }
        }
        private void usingYellowStyleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MarkerStyle YellowStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(180, Color.Yellow)));
            codebox.Selection.SetStyle(YellowStyle);
        }

        private void usingRedStyleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MarkerStyle YellowStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(50, Color.Red)));
            codebox.Selection.SetStyle(YellowStyle);
        }

        private void usingGreenStyleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MarkerStyle YellowStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(50, Color.Green)));
            codebox.Selection.SetStyle(YellowStyle);
        }

        private void usingGrayStyleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MarkerStyle YellowStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(50, Color.Gray)));
            codebox.Selection.SetStyle(YellowStyle);
        }

        private void usingBlueStyleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            MarkerStyle YellowStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(50, Color.Blue)));
            codebox.Selection.SetStyle(YellowStyle);
        }

        private void unmarkSelectionToolStripMenuItem_Click(object sender, EventArgs e)
        {
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = null;
        }

        private void closeToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void goLeftBracketToolStripMenuItem_Click(object sender, EventArgs e)
        {
            codebox.GoLeftBracket('(', ')');
        }

        private void goRightBracketToolStripMenuItem_Click(object sender, EventArgs e)
        {
            codebox.GoRightBracket('(', ')');
        }

        private void goLeftBracketToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            codebox.GoLeftBracket('[', ']');
        }

        private void goRightBracketToolStripMenuItem1_Click(object sender, EventArgs e)
        {
            codebox.GoRightBracket('[', ']');
        }

        private void goLeftBracketToolStripMenuItem2_Click(object sender, EventArgs e)
        {
            codebox.GoLeftBracket('{', '}');
        }

        private void goLeftBracketToolStripMenuItem3_Click(object sender, EventArgs e)
        {
            codebox.GoRightBracket('{', '}');
        }
        #endregion

        #region AutoComplete

        #endregion

        #region Hyperlink
        public bool ShowHiddenCharacters { get; set; }
        
        TextStyle hyperlink = new TextStyle(new SolidBrush(YnoteSettingsBase.CurrentTheme.HyperLinkColor), null, FontStyle.Underline);
        bool CharIsHyperlink(Place place)
        {
            var mask = codebox.GetStyleIndexMask(new Style[] { hyperlink });
            if (place.iChar < codebox.GetLineLength(place.iLine))
                if ((codebox[place].style & mask) != 0)
                    return true;

            return false;
        }

        private void codebox_MouseMove(object sender, MouseEventArgs e)
        {
            var p = codebox.PointToPlace(e.Location);
            if (CharIsHyperlink(p))
            {
                if (e.Button == System.Windows.Forms.MouseButtons.Left && (ModifierKeys & Keys.Control) == Keys.Control)
                    codebox.Cursor = Cursors.Hand;
                else
                    codebox.Cursor = Cursors.Arrow;
            }
            else
               codebox.Cursor = Cursors.IBeam;
        }

        private void codebox_MouseDown(object sender, MouseEventArgs e)
        {
            var p = codebox.PointToPlace(e.Location);
            if (CharIsHyperlink(p))
            {
                try
                {
                    if (e.Button == System.Windows.Forms.MouseButtons.Left && (ModifierKeys & Keys.Control) == Keys.Control)
                    {
                        codebox.Cursor = Cursors.Hand;
                        var url = codebox.GetRange(p, p).GetFragment(@"[\S]").Text;
                        Process.Start(url);
                    }
                }

                catch (Exception ex) { Console.WriteLine(ex.Message); }
            }
        }
        void HighlightInvisibleChars(Range range)
        {
            if (ShowHiddenCharacters)
            {
                range.ClearStyle(invisibleCharsStyle);
                range.SetStyle(invisibleCharsStyle, @".$|.\r\n|\s");
            }
            else
                range.ClearStyle(invisibleCharsStyle);
        }
        private void fctb_textchangeddelayed(object sender, FastColoredTextBoxNS.TextChangedEventArgs e)
        {
             e.ChangedRange.ClearStyle(hyperlink);  
             e.ChangedRange.SetStyle(hyperlink, @"(http|ftp|ftps|https):\/\/[\w\-_]+(\.[\w\-_]+)+([\w\-\.,@?^=%&amp;:/~\+#]*[\w\-\@?^=%&amp;/~\+#])?");
             HighlightInvisibleChars(e.ChangedRange);
             Host.SyntaxHighlighter.HighlightSyntax(e.ChangedRange.tb.Language, e.ChangedRange);
             if (codebox.IsChanged)
                 if (!this.Text.Contains("*"))
                     this.Text += "*";
        }
        #endregion

        #region Dock/Context/Text

        protected MarkerStyle SameWordsStyle = new MarkerStyle(new SolidBrush(Color.FromArgb(40, Color.Gray)));
        private void codebox_SelectionChangedDelayed(object sender, EventArgs e)
        {
            codebox.VisibleRange.ClearStyle(SameWordsStyle);

            if (!codebox.Selection.IsEmpty)
                return;//user selected diapason

            //get fragment around caret
            var fragment = codebox.Selection.GetFragment(@"\w");
            string text = fragment.Text;
            if (text.Length == 0)
                return;
            //highlight same words
            var ranges = codebox.VisibleRange.GetRanges("\\b" + text + "\\b").ToArray();
            if (ranges.Length > 1)
                foreach (var r in ranges)
                    r.SetStyle(SameWordsStyle);
        }
        private void closeAllButThisToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dockPanel = this.DockPanel;
            if (dockPanel.DocumentStyle == DocumentStyle.SystemMdi)
            {
                Form activeMdi = ActiveMdiChild;
                foreach (Form form in MdiChildren)
                {
                    if (form != activeMdi)
                        form.Close();
                }
            }
            else
            {
                foreach (IDockContent document in dockPanel.DocumentsToArray())
                {
                    if (!document.DockHandler.IsActivated)
                        document.DockHandler.Close();
                }
            }
        }

        private void closeAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var dockPanel = this.DockPanel;
            if (dockPanel.DocumentStyle == DocumentStyle.SystemMdi)
            {
                foreach (Form form in MdiChildren)
                    form.Close();
            }
            else
            {
                IDockContent[] documents = dockPanel.DocumentsToArray();
                foreach (IDockContent content in documents)
                    content.DockHandler.Close();
            }
        }

        private void copyFileNameToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(this.Text);
        }

        private void copyFilePathToClipboardToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Clipboard.SetText(this.Name);
        }

        private void openContainingFolderToolStripMenuItem_Click(object sender, EventArgs e)
        {
           string dir= System.IO.Path.GetDirectoryName(this.Name);
           Process.Start(dir);
        }

        private void autoCompleteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.AutoCompleteMenu.Show(true);
        }

        private void selectAllToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.SelectAll();
        }

        private void foldSelectedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.CollapseBlock(codebox.Selection.Start.iLine, codebox.Selection.End.iLine);
        }

        private void unFoldSelectedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.ExpandBlock(codebox.Selection.Start.iLine, codebox.Selection.End.iLine);
        }
        private void findToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.ShowFindDialog();
        }

        private void replaceToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.ShowReplaceDialog();
        }

        private void goToToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.codebox.ShowGoToDialog();
        }

        private void usingDefaultToolStripMenuItem_Click(object sender, EventArgs e)
        {
        }

        private void usingRedToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Color red = Color.FromArgb(50, Color.Red);
            SolidBrush redbrush = new SolidBrush(red);
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = redbrush;
        }

        private void usingYellowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Color red = Color.FromArgb(50, Color.Yellow);
            SolidBrush redbrush = new SolidBrush(red);
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = redbrush;
        }

        private void usingGreenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Color red = Color.FromArgb(50, Color.Green);
            SolidBrush redbrush = new SolidBrush(red);
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = redbrush;
        }

        private void usingGrayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Color red = Color.FromArgb(50, Color.Gray);
            SolidBrush redbrush = new SolidBrush(red);
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = redbrush;
        }

        private void unmarkLineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            codebox[codebox.Selection.Start.iLine].BackgroundBrush = null;
        }
        #endregion

        #region Plugins

        [ImportMany("IContextMenuPlugin")]
        private IEnumerable<IContextMenuPlugin> ContextMenuPlugins { get; set; }

        void LoadIContextMenuPlugin()
        {
            var dircatalog = new DirectoryCatalog(Application.StartupPath + "\\Plugins");
            var container = new CompositionContainer(dircatalog);
            ContextMenuPlugins = container.GetExportedValues<IContextMenuPlugin>();
            foreach (var plugin in ContextMenuPlugins)
            {
                plugin.Parent = this;
                plugin.Dispose();
                this.EditorContext.Items.Add(plugin.MainMenuItem);
            }
        }
        #endregion

    }
}
